{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Interactive Tables and their API\n",
    "\n",
    "The table UI allows column drag/drop, hide, sorting, formatting, searching, selecting/export as CSV.  This makes it easy to paste into a spreadsheet like Excel.\n",
    "\n",
    "There is a menu in the top-left for the whole table, and each column has a menu that appears on hover.\n",
    "\n",
    "There are also keyboard commands: digits change the precision of all columns, shift-digit changes the precision of the current column.  Arrow keys navigate, and the page up/down keys work too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "new TableDisplay( new CSV().read(\"../resources/data/interest-rates.csv\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Formatting and Alignment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import com.twosigma.beakerx.table.*\n",
    "import com.twosigma.beakerx.table.format.TableDisplayStringFormat\n",
    "\n",
    "display = new TableDisplay(new CSV().read(\"../resources/data/interest-rates.csv\"))\n",
    "//show all time columns in days\n",
    "display.setStringFormatForTimes(TimeUnit.DAYS)\n",
    "//min 4, max 6 decimal places for all doubles\n",
    "display.setStringFormatForType(ColumnType.Double, TableDisplayStringFormat.getDecimalFormat(4,6))\n",
    "//setting for a column takes precidence over the type\n",
    "display.setStringFormatForColumn(\"m3\", TableDisplayStringFormat.getDecimalFormat(0, 0))\n",
    "//set the alignment\n",
    "display.setAlignmentProviderForType(ColumnType.Double, TableDisplayAlignmentProvider.RIGHT_ALIGNMENT)\n",
    "display.setAlignmentProviderForColumn('m3', TableDisplayAlignmentProvider.CENTER_ALIGNMENT)\n",
    "\n",
    "//using a closure\n",
    "display.setStringFormatForColumn(\"y3\") { value, row, col, tableDisplay ->\n",
    "   if(value < 8) {\n",
    "       \":(\"\n",
    "   } else {\n",
    "       \":)\"\n",
    "  } \n",
    "}\n",
    "\n",
    "display"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This changes the format of the table above in the previous output cell. It's updated because the object is synchronized between the kernel and the client."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "display.setStringFormatForTimes(TimeUnit.HOURS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bar Charts Renderer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import com.twosigma.beakerx.table.*\n",
    "import com.twosigma.beakerx.table.renderer.TableDisplayCellRenderer\n",
    "\n",
    "def display2 = new TableDisplay(new CSV().read(\"../resources/data/interest-rates.csv\"))\n",
    "//right now, the only renderer option is for data bars\n",
    "display2.setRendererForType(ColumnType.Double, TableDisplayCellRenderer.getDataBarsRenderer())\n",
    "//use the false parameter to hide the String value\n",
    "display2.setRendererForColumn(\"y10\", TableDisplayCellRenderer.getDataBarsRenderer(false))\n",
    "display2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Column Visibility and Placement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import com.twosigma.beakerx.table.*\n",
    "import com.twosigma.beakerx.fileloader.CSV\n",
    "import com.twosigma.beakerx.table.format.TableDisplayStringFormat\n",
    "\n",
    "def display3 = new TableDisplay(new CSV().read(\"../resources/data/interest-rates.csv\"))\n",
    "display3.setStringFormatForType(ColumnType.Double, TableDisplayStringFormat.getDecimalFormat(9,9))\n",
    "//freeze a column\n",
    "display3.setColumnFrozen(\"y1\", true)\n",
    "//hide a column\n",
    "display3.setColumnVisible(\"y30\", false)\n",
    "\n",
    "//explicitly set column order/visiblity\n",
    "display3.setColumnOrder([\"m3\", \"y1\", \"y5\", \"time\", \"y2\"]) //Columns in the list will be shown in the provided order. Columns not in the list will be hidden.\n",
    "display3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Heatmaps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import com.twosigma.beakerx.table.*\n",
    "import com.twosigma.beakerx.table.highlight.TableDisplayCellHighlighter\n",
    "\n",
    "def display4 = new TableDisplay(new CSV().read(\"../resources/data/interest-rates.csv\"))\n",
    "display4.addCellHighlighter(TableDisplayCellHighlighter.getHeatmapHighlighter(\"m3\", TableDisplayCellHighlighter.FULL_ROW))\n",
    "\n",
    "//the following two overloads should also be supported\n",
    "//set the min and max used for calculating the color\n",
    "//display4.addCellHighlighter(TableDisplayCellHighlighter.getHeatmapHighlighter(\"y1\", TableDisplayCellHighlighter.FULL_ROW, 0, 5))\n",
    "//set the colors used for the min and max\n",
    "//display4.addCellHighlighter(TableDisplayCellHighlighter.getHeatmapHighlighter(\"m6\", TableDisplayCellHighlighter.SINGLE_COLUMN, null, null, Color.YELLOW, Color.BLUE))\n",
    "\n",
    "display4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cell Background Color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def map = [\n",
    "   [a:1, b:2, c:3],\n",
    "   [a:4, b:5, c:6],\n",
    "   [a:7, b:8, c:5]\n",
    "]\n",
    "def display5 = new TableDisplay(map)\n",
    "display5.addCellHighlighter { row, column, tableDisplay ->\n",
    "  if (column == 2) {\n",
    "    display5.values[row][column] < 5 ? Color.RED : Color.GREEN\n",
    "  }\n",
    "}\n",
    "display5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Heatmaps per Column"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import com.twosigma.beakerx.table.*\n",
    "import com.twosigma.beakerx.table.highlight.*\n",
    "\n",
    "display6 = new TableDisplay(new CSV().read(\"../resources/data/interest-rates.csv\"))\n",
    "display6.addCellHighlighter(TableDisplayCellHighlighter.getHeatmapHighlighter(\"m3\", 0, 8, Color.ORANGE, Color.PINK))\n",
    "display6.addCellHighlighter(TableDisplayCellHighlighter.getHeatmapHighlighter(\"m6\", TableDisplayCellHighlighter.SINGLE_COLUMN, 6, 8, Color.BLACK, Color.PINK))\n",
    "\n",
    "display6.addCellHighlighter(new ThreeColorHeatmapHighlighter(\"y1\", TableDisplayCellHighlighter.SINGLE_COLUMN, 4, 6, 8, new Color(247,106,106), new Color(239,218,82), new Color(100,189,122)))\n",
    "\n",
    "display6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "display6.removeAllCellHighlighters()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Color Unique Entries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import com.twosigma.beakerx.table.highlight.*\n",
    "\n",
    "def table = new TableDisplay([[1,2,3], \n",
    "                              [3,4,5], \n",
    "                              [6,2,8], \n",
    "                              [6,2,8], \n",
    "                              [6,2,8], \n",
    "                              [6,4,8], \n",
    "                              [6,2,8], \n",
    "                              [6,2,8], \n",
    "                              [6,5,8]], \n",
    "                             ['a', 'b', 'c'], \n",
    "                             ['double', 'double', 'double'])\n",
    "table.addCellHighlighter(TableDisplayCellHighlighter.getUniqueEntriesHighlighter(\"b\", TableDisplayCellHighlighter.FULL_ROW))\n",
    "table"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Font Size, Color, and Vertical Headers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def mapList5 = [\n",
    " [firstCol:1, secondCol:2, thirdCol:3],\n",
    " [firstCol:4, secondCol:5, thirdCol:6],\n",
    " [firstCol:9, secondCol:8, thirdCol:9]\n",
    "]\n",
    "def td4 = new TableDisplay(mapList5)\n",
    "\n",
    "//tool tip can be set with a closure\n",
    "td4.setToolTip { row, col, display ->\n",
    " \"The value is: \" + display.values[row][col]\n",
    "}\n",
    "td4\n",
    "\n",
    "//set the font size and color\n",
    "td4.dataFontSize = 35\n",
    "td4.headerFontSize = 30\n",
    "\n",
    "def colors = [[Color.LIGHT_GRAY, Color.GRAY, Color.RED],\n",
    "            [Color.DARK_GREEN, Color.ORANGE, Color.RED],\n",
    "            [Color.MAGENTA, Color.BLUE, Color.BLACK]]\n",
    "\n",
    "td4.setFontColorProvider { row, col, td ->\n",
    " colors[row][col]\n",
    "}\n",
    "\n",
    "//try different filter options\n",
    "td4.setRowFilter { row, model ->\n",
    " //model[row][1] == 8\n",
    " true\n",
    " //false\n",
    " //model[row][0] == model[row][2]\n",
    "}\n",
    "\n",
    "//set vertical headers\n",
    "//you can also do this in the right-click menu\n",
    "td4.setHeadersVertical(true)\n",
    "\n",
    "td4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Programmable Actions\n",
    "\n",
    "Bind a closure or running a cell to the context menu or double click action on the table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "abc = 0; // test variable\n",
    "mapList = [\n",
    "   [a:1, b:2, c:3],\n",
    "   [a:4, b:5, c:6],\n",
    "   [a:7, b:8, c:5]\n",
    "]\n",
    "OutputCell.HIDDEN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def display1 = new TableDisplay(mapList)\n",
    "//set what happens on a double click\n",
    "display1.setDoubleClickAction { row, col, tableDisplay ->\n",
    "   tableDisplay.values[row][col] = tableDisplay.values[row].sum()\n",
    "}\n",
    "\n",
    "//add a context menu item\n",
    "display1.addContextMenuItem(\"negate\") { row, col, tableDisplay ->\n",
    "   tableDisplay.values[row][col] = -tableDisplay.values[row][col]\n",
    "}\n",
    "\n",
    "display1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def display2 = new TableDisplay(mapList)\n",
    "\n",
    "//run tagged cell on action\n",
    "display2.addContextMenuItem(\"run print cell\", \"print_cell\");\n",
    "display2.setDoubleClickAction(\"print_cell\");\n",
    "\n",
    "display2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "print_cell"
    ]
   },
   "outputs": [],
   "source": [
    "abc++\n",
    "println abc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "display4 = new TableDisplay(mapList)\n",
    "\n",
    "//run tagged cell on action\n",
    "display4.addContextMenuItem(\"run tagged_cell cell\", \"tagged_cell\");\n",
    "display4.setDoubleClickAction(\"tagged_cell\");\n",
    "\n",
    "display4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "tagged_cell"
    ]
   },
   "outputs": [],
   "source": [
    "def details = display4.details\n",
    "\n",
    "if(details != null){\n",
    "  switch(details.actionType){\n",
    "    case 'DOUBLE_CLICK':\n",
    "    print (\"You clicked on the cell [\" + details.row + \", \" + details.col + \"]\")\n",
    "      break;\n",
    "    case 'CONTEXT_MENU_CLICK':\n",
    "     print (\"You selected context menu '\" + details.contextMenuItem + \"' on the cell [\" + details.row + \", \" + details.col + \"]\")\n",
    "    break;\n",
    "  }\n",
    "}else{\n",
    "  println \"no table tag action performed.\"\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTML format\n",
    "\n",
    "HTML format allows markup and styling of the cell's content.  Interactive JavaScript and images are not supported however.  See below for image support."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tbl = new TableDisplay([x: '<em style=\"color:red\">italic red</em>',\n",
    "                      y: '<b style=\"color:blue\">bold blue</b>',\n",
    "                      z: 'multiline strings<br/>work fine too']);\n",
    "tbl.setStringFormatForColumn(\"Value\", TableDisplayStringFormat.getHTMLFormat())\n",
    "tbl"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Image format\n",
    "\n",
    "Image format allows loading any image supported by the web browser, including PNG, JPG, and SVG formats.  It can be a relative URL served from the same directories as the notebook itself, an absolute URL to anywhere on the web, or a data URL with encoded data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tbl = new TableDisplay(['Peacock': '../resources/img/butterfly1.jpg',\n",
    "                        'Morpho': '../resources/img/butterfly2.jpg',\n",
    "                        'Swallowtail': '../resources/img/butterfly3.jpg']);\n",
    "tbl.setStringFormatForColumn(\"Value\", TableDisplayStringFormat.getImageFormat())\n",
    "tbl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tbl = new TableDisplay(['https://en.wikipedia.org/wiki/IPython': 'http://jupyter.org/assets/nav_logo.svg',\n",
    "                        'https://en.wikipedia.org/wiki/Two_Sigma': 'https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/2_sigma_logo.png/150px-2_sigma_logo.png']);\n",
    "tbl.setStringFormatForColumn(\"Value\", TableDisplayStringFormat.getImageFormat())\n",
    "tbl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tbl = new TableDisplay(['up': '',\n",
    "                        'down': '']);\n",
    "tbl.setStringFormatForColumn(\"Value\", TableDisplayStringFormat.getImageFormat())\n",
    "tbl"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Auto linking of URLs\n",
    "\n",
    "The normal string format automatically detects URLs and links them.  An underline appears when the mouse hovers over such a string, and when you click it opens in a new window."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "new TableDisplay(['Two Sigma': 'http://twosigma.com', 'BeakerX': 'surrounding text http://BeakerX.com works fine'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Seamless Update"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def mapListToUpdate = [\n",
    "   [a:1, b:2, c:3],\n",
    "   [a:4, b:5, c:6],\n",
    "   [a:7, b:8, c:9]\n",
    "]\n",
    "tableToUpdate = new TableDisplay(mapListToUpdate)\n",
    "\n",
    "tableToUpdate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tableToUpdate.values[0][0] = 99\n",
    "tableToUpdate.sendModel()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tableToUpdate.updateCell(2,\"c\",121)\n",
    "tableToUpdate.sendModel()"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "beakerx_kernel_parameters": {},
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Groovy",
   "language": "groovy",
   "name": "groovy"
  },
  "language_info": {
   "codemirror_mode": "groovy",
   "file_extension": ".groovy",
   "mimetype": "",
   "name": "Groovy",
   "nbconverter_exporter": "",
   "version": "2.4.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": false,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": false,
   "toc_window_display": false
  },
  "widgets": {
   "state": {
    "f7d30807-6d2d-414d-b604-fcb0ed748612": {
     "views": [
      {
       "cell_index": 0
      },
      {
       "cell_index": 1
      }
     ]
    }
   },
   "version": "1.2.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
